package com.jusfoun.communication.dao.impl;

import com.jusfoun.communication.constant.RedisKeyConstant;
import com.jusfoun.communication.constant.UserConstant;
import com.jusfoun.communication.dao.UserDao;
import com.jusfoun.communication.model.User;
import com.jusfoun.communication.util.PageInfo;
import com.jusfoun.communication.util.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author player six
 * @email jslcylcy@163.com
 * @create 2017-12-12 16:44
 **/
@Component
public class UserDaoImpl implements UserDao {

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public String generateToken(String appId, String userName) {
        String orgin = redisUtil.getHashKey(RedisKeyConstant.REDIS_USERINFO + appId + ":" + userName, UserConstant.REDIS_STORE_TOKEN);
        if (StringUtils.isNotBlank(orgin)) {
            redisUtil.delHashKey(RedisKeyConstant.REDIS_USERINFO + appId + ":" + userName, UserConstant.REDIS_STORE_TOKEN);
            redisUtil.deleteItem(RedisKeyConstant.REDIS_TOKEN + orgin);
        }

        String token = UUID.randomUUID().toString();
        redisUtil.saveHashKey(RedisKeyConstant.REDIS_TOKEN + token, UserConstant.REDIS_STORE_APPID, appId);
        redisUtil.saveHashKey(RedisKeyConstant.REDIS_TOKEN + token, UserConstant.REDIS_STORE_USERNAME, userName);
        redisUtil.saveHashKey(RedisKeyConstant.REDIS_USERINFO + appId + ":" + userName, UserConstant.REDIS_STORE_TOKEN, token);
        redisUtil.expireItem(RedisKeyConstant.REDIS_TOKEN + token, 7, TimeUnit.DAYS);
        return token;
    }

    @Override
    public List<String> listAllUserName(String appId) {
        long total = redisUtil.zSize(RedisKeyConstant.REDIS_APPINFO + appId + RedisKeyConstant.REDIS_APP_USERS);
        if (total > 0) {
            return new ArrayList<>(redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_APPINFO + appId + RedisKeyConstant.REDIS_APP_USERS, 0, total, true));
        } else {
            return new ArrayList<>();
        }
    }

    @Override
    public long listTagCount(String appId) {
        return redisUtil.zSize(RedisKeyConstant.REDIS_TAGLIST + appId);
    }

    @Override
    public List<String> listTag(String appId, PageInfo pageInfo) {
        if (pageInfo.getStartRow() > 0 && pageInfo.getEndRow() >= pageInfo.getStartRow()) {
            return new ArrayList<>(redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_TAGLIST + appId, pageInfo.getStartRow() - 1L, pageInfo.getEndRow() - pageInfo.getStartRow() + 1L, true));
        } else {
            return new ArrayList<>(redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_TAGLIST + appId, 0, pageInfo.getTotal(), true));
        }
    }

    @Override
    public long listGroupCount(String appId) {
        return redisUtil.zSize(RedisKeyConstant.REDIS_GROUPLIST + appId);
    }

    @Override
    public List<String> listGroup(String appId, PageInfo pageInfo) {
        if (pageInfo.getStartRow() > 0 && pageInfo.getEndRow() >= pageInfo.getStartRow()) {
            return new ArrayList<>(redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_GROUPLIST + appId, pageInfo.getStartRow() - 1L, pageInfo.getEndRow() - pageInfo.getStartRow() + 1L, true));
        } else {
            return new ArrayList<>(redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_GROUPLIST + appId, 0, pageInfo.getTotal(), true));
        }
    }

    @Override
    public long listGroupUserCount(String appId, String group) {
        return redisUtil.zSize(RedisKeyConstant.REDIS_GROUP + appId + RedisKeyConstant.REDIS_APP_GROUP_USER + group);
    }

    @Override
    public List<String> listGroupUser(String appId, String group, PageInfo pageInfo) {
        if (pageInfo.getStartRow() > 0 && pageInfo.getEndRow() >= pageInfo.getStartRow()) {
            return new ArrayList<>(redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_GROUP + appId + RedisKeyConstant.REDIS_APP_GROUP_USER + group, pageInfo.getStartRow() - 1L, pageInfo.getEndRow() - pageInfo.getStartRow() + 1L, true));
        } else {
            return new ArrayList<>(redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_GROUP + appId + RedisKeyConstant.REDIS_APP_GROUP_USER + group, 0, pageInfo.getTotal(), true));
        }
    }

    @Override
    public Set<String> listGroupUser(String appId, String group) {
        return redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_GROUP + appId + RedisKeyConstant.REDIS_APP_GROUP_USER + group, 0, listGroupUserCount(appId, group), true);
    }

    @Override
    public long listUserCount(String appId) {
        return redisUtil.zSize(RedisKeyConstant.REDIS_APPINFO + appId + RedisKeyConstant.REDIS_APP_USERS);
    }

    @Override
    public List<User> listUser(String appId, PageInfo pageInfo) {
        Set<String> userSet;
        if (pageInfo.getStartRow() > 0 && pageInfo.getEndRow() >= pageInfo.getStartRow()) {
            userSet = redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_APPINFO + appId + RedisKeyConstant.REDIS_APP_USERS, pageInfo.getStartRow() - 1L, pageInfo.getEndRow() - pageInfo.getStartRow() + 1L, true);
        } else {
            userSet = redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_APPINFO + appId + RedisKeyConstant.REDIS_APP_USERS, 0, pageInfo.getTotal(), true);
        }

        List<User> userList = new ArrayList<>();
        for (String user : userSet) {
            User u = new User();
            u.setUserName(user);
            u.setGroup(redisUtil.getHashKey(RedisKeyConstant.REDIS_USERINFO + appId + ":" + user, UserConstant.REDIS_STORE_GROUP));
            u.setTags(redisUtil.getHashKey(RedisKeyConstant.REDIS_USERINFO + appId + ":" + user, UserConstant.REDIS_STORE_TAGS));
            userList.add(u);
        }
        return userList;
    }

    @Override
    public long listTagUserCount(String appId, String tag) {
        return redisUtil.zSize(RedisKeyConstant.REDIS_TAG + appId + RedisKeyConstant.REDIS_APP_TAG_USER + tag);
    }

    @Override
    public List<String> listTagUser(String appId, String tag, PageInfo pageInfo) {
        Set<String> userSet;
        if (pageInfo.getStartRow() > 0 && pageInfo.getEndRow() >= pageInfo.getStartRow()) {
            userSet = redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_TAG + appId + RedisKeyConstant.REDIS_APP_TAG_USER + tag, pageInfo.getStartRow() - 1L, pageInfo.getEndRow() - pageInfo.getStartRow() + 1L, true);
        } else {
            userSet = redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_TAG + appId + RedisKeyConstant.REDIS_APP_TAG_USER + tag, 0, pageInfo.getTotal(), true);
        }
        return new ArrayList<>(userSet);
    }

    @Override
    public Set<String> listTagUser(String appId, String tag) {
        return redisUtil.zRangeByIdx(RedisKeyConstant.REDIS_TAG + appId + RedisKeyConstant.REDIS_APP_TAG_USER + tag, 0, listTagUserCount(appId, tag), true);
    }

    @Override
    public void addTagNotExist(String appId, String tag) {
        Long rank = redisUtil.zRank(RedisKeyConstant.REDIS_TAGLIST + appId, tag);
        if (rank == null || rank < 0) {
            redisUtil.zAdd(RedisKeyConstant.REDIS_TAGLIST + appId, tag, System.currentTimeMillis());
        }
    }

    @Override
    public void updateUserTags(String appId, String userName, String[] tags) {
        Cursor<ZSetOperations.TypedTuple<String>> cursor = redisUtil.zScan(RedisKeyConstant.REDIS_TAGLIST + appId, ScanOptions.NONE);
        try {
            while (cursor.hasNext()) {
                //获取所有的tag,移除当前用户
                ZSetOperations.TypedTuple<String> item = cursor.next();
                if (StringUtils.isNotBlank(item.getValue())) {
                    redisUtil.zRemove(RedisKeyConstant.REDIS_TAG + appId + RedisKeyConstant.REDIS_APP_TAG_USER + item.getValue(), userName);
                }
            }
        } finally {
            if (null != cursor) {
                redisUtil.close(cursor);
            }
        }
        if (tags != null && tags.length > 0) {
            //将当前用户添加到指定的tag
            for (String tag : tags) {
                redisUtil.zAdd(RedisKeyConstant.REDIS_TAG + appId + RedisKeyConstant.REDIS_APP_TAG_USER + tag, userName, System.currentTimeMillis());
            }
            //添加当前用户的tag信息
            redisUtil.saveHashKey(RedisKeyConstant.REDIS_USERINFO + appId + ":" + userName, UserConstant.REDIS_STORE_TAGS, StringUtils.join(tags, ","));
        } else {
            redisUtil.saveHashKey4Null(RedisKeyConstant.REDIS_USERINFO + appId + ":" + userName, UserConstant.REDIS_STORE_TAGS, "");
        }
    }

    @Override
    public void addGroupNotExist(String appId, String group) {
        Long rank = redisUtil.zRank(RedisKeyConstant.REDIS_GROUPLIST + appId, group);
        if (rank == null || rank < 0) {
            redisUtil.zAdd(RedisKeyConstant.REDIS_GROUPLIST + appId, group, System.currentTimeMillis());
        }
    }

    @Override
    public void updateUserGroup(String appId, String userName, String group) {
        Cursor<ZSetOperations.TypedTuple<String>> cursor = redisUtil.zScan(RedisKeyConstant.REDIS_GROUPLIST + appId, ScanOptions.NONE);
        try {
            while (cursor.hasNext()) {
                //获取所有的分组,移除当前用户
                ZSetOperations.TypedTuple<String> item = cursor.next();
                if (StringUtils.isNotBlank(item.getValue())) {
                    redisUtil.zRemove(RedisKeyConstant.REDIS_GROUP + appId + RedisKeyConstant.REDIS_APP_GROUP_USER + item.getValue(), userName);
                }
            }
        } finally {
            if (null != cursor) {
                redisUtil.close(cursor);
            }
        }
        if (StringUtils.isNotBlank(group)) {
            //将当前用户添加到指定的分组
            redisUtil.zAdd(RedisKeyConstant.REDIS_GROUP + appId + RedisKeyConstant.REDIS_APP_GROUP_USER + group, userName, System.currentTimeMillis());
            //添加当前用户的分组信息
            redisUtil.saveHashKey(RedisKeyConstant.REDIS_USERINFO + appId + ":" + userName, UserConstant.REDIS_STORE_GROUP, group);
        } else {
            redisUtil.saveHashKey4Null(RedisKeyConstant.REDIS_USERINFO + appId + ":" + userName, UserConstant.REDIS_STORE_GROUP, "");
        }
    }

    @Override
    public void addUserNotExist(String appId, String userName) {
        Long rank = redisUtil.zRank(RedisKeyConstant.REDIS_APPINFO + appId + RedisKeyConstant.REDIS_APP_USERS, userName);
        if (rank == null || rank < 0) {
            redisUtil.zAdd(RedisKeyConstant.REDIS_APPINFO + appId + RedisKeyConstant.REDIS_APP_USERS, userName, System.currentTimeMillis());
        }
    }

    @Override
    public User getUserByToken(String token) {
        String appId = redisUtil.getHashKey(RedisKeyConstant.REDIS_TOKEN + token, UserConstant.REDIS_STORE_APPID);
        String userName = redisUtil.getHashKey(RedisKeyConstant.REDIS_TOKEN + token, UserConstant.REDIS_STORE_USERNAME);
        if (StringUtils.isNotBlank(appId) && StringUtils.isNotBlank(userName)) {
            User u = new User();
            u.setAppId(appId);
            u.setUserName(userName);
            return u;
        }
        return null;
    }
}
